package com.jdd.modules.parking.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jdd.common.api.vo.ResultVo;
import com.jdd.common.constant.IsTransferKind;
import com.jdd.common.constant.JddConstant;
import com.jdd.common.constant.ParkIsOutKind;
import com.jdd.common.exception.JddException;
import com.jdd.common.util.DateUtil;
import com.jdd.common.util.RestTemplateUtils;
import com.jdd.common.util.ResultCode;
import com.jdd.common.util.SnowFlakeUtil;
import com.jdd.modules.configcartype.entity.ConfigCarType;
import com.jdd.modules.configcartype.service.IConfigCarTypeService;
import com.jdd.modules.parking.entity.ParkArmInfo;
import com.jdd.modules.parking.entity.ParkCarEnterLog;
import com.jdd.modules.parking.entity.ParkWorkStation;
import com.jdd.modules.parking.mapper.ParkArmInfoMapper;
import com.jdd.modules.parking.mapper.ParkCarEnterLogMapper;
import com.jdd.modules.parking.mapper.ParkWorkStationMapper;
import com.jdd.modules.parking.service.IparkCarEnterLogService;
import com.jdd.modules.parking.vo.MontiorSearchRespVo;
import com.jdd.modules.parking.vo.MontiorSearchVo;
import com.jdd.modules.sdkidentifyresultpushlog.entity.SdkIdentifyResultPushLog;
import com.jdd.modules.sdkidentifyresultpushlog.entity.vo.MonthlyEnterTypeVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 车辆入场信息
 * @Author: jdd
 * @Date: 2021-02-20
 * @Version: V1.0
 */
@Slf4j
@Service
public class ParkCarEnterLogServiceImpl extends ServiceImpl<ParkCarEnterLogMapper, ParkCarEnterLog> implements IparkCarEnterLogService {

    @Resource
    private ParkCarEnterLogMapper parkCarEnterLogMapper;
    @Resource
    private ParkArmInfoMapper parkArmInfoMapper;
    @Resource
    private ParkWorkStationMapper parkWorkStationMapper;
    @Resource
    private IConfigCarTypeService carTypeService;
    @Resource
    private RestTemplateUtils restTemplateUtils;

    private String springCloudUrl;

    /**
     * 查询停车场的详细入场纪录
     *
     * @return 停车场详细入场纪录
     **/
    @Override
    public List<ParkCarEnterLog> enterLogDetails() {
        return parkCarEnterLogMapper.enterLogDetails();
    }

    /**
     * 查询车辆在指定车场最近的一条未出场的入场记录
     *
     * @param parkCode 车场编号
     * @param plateNo  车牌号
     * @param isOut    出场状态
     * @return 车辆最近的一条入场记录
     * @author renshangyi
     * @date 2021-08-17
     */
    @Override
    public ParkCarEnterLog getLastCarEnterLog(String parkCode, String plateNo, String isOut) {
        LambdaQueryWrapper<ParkCarEnterLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ParkCarEnterLog::getParkCode, parkCode);
        queryWrapper.eq(ParkCarEnterLog::getPlateNo, plateNo);
        queryWrapper.eq(ParkCarEnterLog::getIsOut, isOut);
        queryWrapper.orderByDesc(ParkCarEnterLog::getCarEnterTime);
        List<ParkCarEnterLog> carEnterLogs = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(carEnterLogs)) {
            return null;
        }
        return carEnterLogs.get(0);
    }

    /**
     * 监控页面的入场
     *
     * @param montiorSearchVo
     * @param pageNo
     * @param pageSize
     * @return
     * @throws Exception
     */
    @Override
    public Page<MontiorSearchRespVo> monitorList(MontiorSearchVo montiorSearchVo, Integer pageNo, Integer pageSize) throws Exception {
        if (ObjectUtil.isEmpty(montiorSearchVo)) {
            throw new Exception("查询参数为空");
        }
        if (ObjectUtil.isEmpty(montiorSearchVo.getWorkName())) {
            throw new Exception("查询工作站名称参数为空");
        }
        //车道序列号
        List<String> serials = null;
        //工作站
        List<ParkWorkStation> workStations = parkWorkStationMapper.selectList(new QueryWrapper<ParkWorkStation>().eq("work_name", montiorSearchVo.getWorkName()));
        if (ObjectUtil.isEmpty(workStations)) {
            throw new Exception("工作站不存在！！！");
        }
        //车道名称查询
        if (ObjectUtil.isEmpty(montiorSearchVo.getArmName())) {
            //通过选择车道查询
            String workName = montiorSearchVo.getWorkName();
            QueryWrapper<ParkArmInfo> armInfoQueryWrapper = new QueryWrapper<>();
            armInfoQueryWrapper.eq("work_station_name", workName);
            armInfoQueryWrapper.eq("arm_type", JddConstant.StringNumber.STRING_ZERO);
            List<ParkArmInfo> parkArmInfos = parkArmInfoMapper.selectList(armInfoQueryWrapper);
            if (ObjectUtil.isEmpty(parkArmInfos)) {
                throw new Exception("通过工作站查询车道时为空");
            }
            serials = parkArmInfos.stream().map(ParkArmInfo::getSerialNo).collect(Collectors.toList());
        }
        //时间选择，应该大于当前工作站上班时间
        if (ObjectUtil.isEmpty(montiorSearchVo.getBeginTime())) {
            montiorSearchVo.setBeginTime(workStations.get(0).getUpTime());
        }
        //符合条件的入场纪录，这里要转换成传到前端的集合
        List<MontiorSearchRespVo> montiorSearchRespVos = parkCarEnterLogMapper.monitorList(montiorSearchVo.getPlateNo(), montiorSearchVo.getArmName(), serials, montiorSearchVo.getSwichType(), montiorSearchVo.getBeginTime(), montiorSearchVo.getEndTime(), pageNo, pageSize);
        List<MontiorSearchRespVo> monitorListAll = parkCarEnterLogMapper.monitorListAll(montiorSearchVo.getBeginTime());
        //加上备注和操作员的信息，转换车辆类型
        List<MontiorSearchRespVo> searchRespVos = montiorSearchRespVos.stream().map(item -> {
            if (ObjectUtil.isNotEmpty(item.getInArmName())) {
                ParkArmInfo armByName = parkArmInfoMapper.getArmByName(item.getInArmName());
                if (ObjectUtil.isNotEmpty(armByName) && JddConstant.StringNumber.STRING_ONE.equals(armByName.getArmType())) {
                    item.setRemark("补录的入场记录");
                }
            }
            item.setWorkName(workStations.get(0).getWorkName());
            item.setOperationName(workStations.get(0).getOperationName());
            item.setCarTypeName(getCarType(Integer.parseInt(item.getCarTypeName())));
            return item;
        }).collect(Collectors.toList());
        //转换成分页
        Page<MontiorSearchRespVo> page = new Page<>(pageNo, pageSize);
        //当前显示的数据
        page.setRecords(searchRespVos);
        //总页数
        page.setTotal(searchRespVos.size());
        //有多少页
        page.setPages(0L);
        if (ObjectUtil.isNotEmpty(monitorListAll) && ObjectUtil.isNotEmpty(montiorSearchRespVos)) {
            if (monitorListAll.size() % montiorSearchRespVos.size() > 0) {
                page.setPages(monitorListAll.size() / montiorSearchRespVos.size() + 1);
            } else {
                page.setPages(monitorListAll.size() / montiorSearchRespVos.size());
            }
        }
        return page;
    }

    /**
     * 查询车辆在指定车场的所有未出场的入场纪录，并根据入场时间，倒序排序
     *
     * @param plateNo 车牌号
     * @param isOut   出入场状态
     * @return 车辆未出场的入场纪录
     * @author liuyaowen
     * @date 2021-08-19
     */
    @Override
    public List<ParkCarEnterLog> getCarEnterLogs(String plateNo, String isOut) {
        LambdaQueryWrapper<ParkCarEnterLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ParkCarEnterLog::getPlateNo, plateNo);
        queryWrapper.eq(ParkCarEnterLog::getIsOut, isOut);
        queryWrapper.orderByDesc(ParkCarEnterLog::getCarEnterTime);
        return this.list(queryWrapper);
    }

    /**
     * 得到车辆最新一批入场纪录,车牌号不为空时,查询有牌车的入场纪录,车牌号为空时,查询无牌车的入场纪录
     *
     * @param plateNo  车牌号
     * @param parkCode 车场编号
     * @return 入场纪录
     */
    @Override
    public List<ParkCarEnterLog> getParkCarEnterLog(String plateNo, String parkCode) {
        List<ParkCarEnterLog> carAllEnterLogs = new ArrayList<>();
        // 得到车辆所有的入场纪录
        if (StringUtils.isNotBlank(plateNo)) {
            carAllEnterLogs = parkCarEnterLogMapper.getCarAllNoOutEnterLogs(parkCode, plateNo);
        }
        if (CollectionUtil.isEmpty(carAllEnterLogs)) {
            throw new JddException(ResultCode.PARAMETER_ERROR, "车辆入场纪录不存在");
        }
        // 得到车辆最近的一次入场纪录
        ParkCarEnterLog carLatestEnterLog = carAllEnterLogs.get(carAllEnterLogs.size() - 1);
        // 得到最近的相同入场批次的入场纪录。
        carAllEnterLogs = carAllEnterLogs.stream().filter(enterLog -> enterLog.getBatchNumber().equals(carLatestEnterLog.getBatchNumber())).collect(Collectors.toList());
        // 对入场纪录,针对不同的大小车场,进行去重
        // 定义返回列表
        List<ParkCarEnterLog> nonRepetitiveEnterLogs = new ArrayList<>();
        // 遍历入场纪录列表
        for (ParkCarEnterLog parkCarEnterLog : carAllEnterLogs) {
            if (nonRepetitiveEnterLogs.size() == 0) {
                // 如果返回列表为空,将第一条入场纪录放入
                nonRepetitiveEnterLogs.add(parkCarEnterLog);
            } else {
                // 如果返回列表不为空,获取返回结果中的最后一条入场纪录
                ParkCarEnterLog lastCarEnterLog = nonRepetitiveEnterLogs.get(nonRepetitiveEnterLogs.size() - 1);
                // 如果该条纪录的设备序列号与遍历到的入场纪录的设备序列号不同,将被遍历到的入场纪录放入返回列表
                if (!judgeParkCarEnterLogIsSame(lastCarEnterLog, parkCarEnterLog)) {
                    nonRepetitiveEnterLogs.add(parkCarEnterLog);
                } else {
                    nonRepetitiveEnterLogs.set(nonRepetitiveEnterLogs.size() - 1, parkCarEnterLog);
                }
            }
        }
        return nonRepetitiveEnterLogs;
    }

    /**
     * 根据车辆编号、车牌类型匹配停车场的车辆类型Id
     *
     * @param type 车牌类型
     * @return java.lang.String
     * @version 1.0
     */
    private String getCarType(int type) {
        String result = null;
        List<ConfigCarType> list = carTypeService.getCarTypeList();
        for (ConfigCarType carType : list) {
            String carTypeName = carType.getCarTypeName();
            List<String> typeNo = Arrays.asList(carType.getCarTypeNo().split(","));
            if (typeNo.contains(type + "")) {
                result = carTypeName;
                return result;
            }
        }
        return result;
    }

    /**
     * 更新车辆转区域更新车辆在当前区域的状态
     *
     * @param areaId
     * @param plateNo
     * @return
     */
    @Override
    public boolean updateCarEnterLogThisArea(String areaId, String plateNo) {
        // 将要转卡的记录在当前区域停靠状态改为未停靠
        parkCarEnterLogMapper.updateCarEnterLogThisArea(areaId, plateNo);
        return true;
    }

    /**
     * 查询在本区域内需要转卡的临时车辆信息
     *
     * @param areaId
     * @param plateNo
     * @return
     */
    @Override
    public List<ParkCarEnterLog> getThisAreaCarEnterList(String areaId, List<String> plateNo) {
        return parkCarEnterLogMapper.getGroupByMaxCarEnterLog(areaId, plateNo);
    }


    /**
     * 判定入场纪录是否重复
     */
    private boolean judgeParkCarEnterLogIsSame(ParkCarEnterLog judgedLog, ParkCarEnterLog comparedLog) {
        if (judgedLog.getSerialNo().equals(comparedLog.getSerialNo())) {
            // 如果设备序列号相同
            if (!judgedLog.getCarTypeId().equals(comparedLog.getCarTypeId())) {
                // 如果系统车辆类型不同，则说明存在转卡，该情况用于判断，场内临时车辆转卡
                return false;
            }
            if (!judgedLog.getParkNumber().equals(comparedLog.getParkNumber())) {
                // 如果车位号不同，则说明存在转卡，该情况用于判断，场内车辆注销换套餐。
                return false;
            }
            return true;
        } else {
            // 如果两条入场纪录的设备序列号不同，则说明两条入场纪录是从不同的车道产生的，纪录不重复。
            return false;
        }
    }

    /**
     * 保存预转卡记录
     *
     * @param parkCarEnterLog
     * @param vo
     * @return
     */
    @Override
    public ParkCarEnterLog saveTransferCardInfo(ParkCarEnterLog parkCarEnterLog, MonthlyEnterTypeVo vo) {
        parkCarEnterLog.setId(SnowFlakeUtil.getId());
        parkCarEnterLog.setCarTypeId(vo.getTransferCarTypeId());
        parkCarEnterLog.setCarTypeName(vo.getTransferCarTypeName());
        parkCarEnterLog.setCarNature(JddConstant.StringNumber.STRING_ONE);
        parkCarEnterLog.setParkNumber(vo.getParkNumber());
        parkCarEnterLog.setCreateTime(new Date());
        parkCarEnterLog.setIsTransfer(IsTransferKind.NO_TRANSFER.value());
        parkCarEnterLog.setCarEnterTime(vo.getCarEnterTime());
        parkCarEnterLog.setMonthlyCardId(vo.getMonthlyCardId());
        this.save(parkCarEnterLog);
        return parkCarEnterLog;
    }

    /**
     * 更新预转卡记录
     *
     * @param identifyResultPushLog
     * @param areaId
     * @return
     */
    @Override
    public boolean updateIsTransfer(SdkIdentifyResultPushLog identifyResultPushLog, String areaId) {
        log.info("预转卡记录->{}", JSON.toJSONString(identifyResultPushLog));
        UpdateWrapper<ParkCarEnterLog> updateWrapper = new UpdateWrapper<ParkCarEnterLog>();
        updateWrapper.eq("park_code", identifyResultPushLog.getParkCode());
        updateWrapper.eq("plate_no", identifyResultPushLog.getLicense());
        updateWrapper.eq("is_out", ParkIsOutKind.NOT_PRESENT.value());
        updateWrapper.eq("is_transfer", IsTransferKind.NO_TRANSFER.value());
        updateWrapper.eq("area_id", areaId);
        updateWrapper.le("car_enter_time", DateUtil.dateToString(new Date(), DateUtil.FORMAT_DATETIME_YYYY_MM_DD));
        updateWrapper.set("is_transfer", IsTransferKind.TOO_TRANSFER.value());
        updateWrapper.set("update_time", new Date());
        updateWrapper.set("this_area", JddConstant.IntNumber.INT_ZERO);
        return this.update(updateWrapper);
    }


    /**
     * 无牌车入场识别放入缓存 保留代码
     *
     * @param parkCode
     * @param serialNo
     */
    @Override
    public void sendUnlicensedCarEnter(String parkCode, String serialNo) {
        //组装参数
        MultiValueMap<String, String> request = new LinkedMultiValueMap<>();
        request.add("parkCode", parkCode);
        request.add("plateNo", "无牌车");
        request.add("serialNo", serialNo);
        HttpHeaders headers = new HttpHeaders();
        // 以表单的方式提交
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<MultiValueMap<String, String>> requests = new HttpEntity<>(request, headers);
        String urls = "";
        ResponseEntity<ResultVo> posts = null;
        try {
            posts = restTemplateUtils.post(urls, requests, ResultVo.class);
            if (posts != null) {
                ResultVo body = posts.getBody();
                if (null != body && body.getCode() == 200) {
                    log.info("无牌车入场识别信息放入云端缓存,成功");
                }
            }
        } catch (Exception e) {
            log.error("无牌车入场识别信息放入云端缓存接口异常", e.getMessage());
        }
    }

    /**
     * 查询指定车场最近的一条未出场的入场记录
     *
     * @param parkCode 车场编号
     * @param isOut    出场状态
     * @return 车辆最近的一条入场记录
     * @author renshangyi
     * @date 2021-08-17
     */
    @Override
    public ParkCarEnterLog getLastCarEnterLogNoPlateNo(String parkCode, String isOut) {
        LambdaQueryWrapper<ParkCarEnterLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ParkCarEnterLog::getParkCode, parkCode);
        queryWrapper.eq(ParkCarEnterLog::getIsOut, isOut);
        queryWrapper.orderByDesc(ParkCarEnterLog::getCarEnterTime);
        List<ParkCarEnterLog> carEnterLogs = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(carEnterLogs)) {
            return null;
        }
        return carEnterLogs.get(0);
    }

	@Override
	public List<ParkCarEnterLog> getNewParkCarEnterLogGroupByPlateNo() {
		return parkCarEnterLogMapper.getNewParkCarEnterLogGroupByPlateNo();
	}

	@Override
    public List<ParkCarEnterLog> getIsNotOutCarEnterLog(String parkCode, String plateNo, String isOut) {
        LambdaQueryWrapper<ParkCarEnterLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ParkCarEnterLog::getParkCode,parkCode);
        queryWrapper.eq(ParkCarEnterLog::getPlateNo,plateNo);
        queryWrapper.eq(ParkCarEnterLog::getIsOut,isOut);
        queryWrapper.orderByDesc(ParkCarEnterLog::getCarEnterTime);
        return this.list(queryWrapper);
    }

    @Override
    public Date getOneEnterTime(String parkCode, String plateNo) {
       return parkCarEnterLogMapper.getOneEnterTime(parkCode,plateNo);
    }

    @Override
    public void updateSynchronousStatus(String id, String synchronousStatus) {
        ParkCarEnterLog parkCarEnterLog = new ParkCarEnterLog();
        parkCarEnterLog.setId(id);
        parkCarEnterLog.setSynchronousStatus(synchronousStatus);
        parkCarEnterLogMapper.updateById(parkCarEnterLog);
    }

    @Override
    public List<ParkCarEnterLog> getAreaNoOutCarEnterLog(List<String> plateNos,String areaId,String parkCode){
        LambdaQueryWrapper<ParkCarEnterLog> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(ParkCarEnterLog::getPlateNo,plateNos);
        queryWrapper.eq(ParkCarEnterLog::getAreaId,areaId);
        queryWrapper.eq(ParkCarEnterLog::getIsOut,"0");
        queryWrapper.eq(ParkCarEnterLog::getParkCode,parkCode);
        queryWrapper.eq(ParkCarEnterLog::getThisArea,"1");
        queryWrapper.orderByAsc(ParkCarEnterLog::getCarEnterTime);
        return this.list(queryWrapper);
    }
}
